// stack and heap
// stack
//   fast allocations with zero administrative overhead
//   grows and shrinks with the function call depth
//   every called function has its stack segment that remains valid until the function returns
//   a reference to a stack object becomes invalid on function return
//   stack space is limited, typically to a few megabytes per thread
// heap
//   a large memory area that is administrated by OS
//   heap objects are allocated and freed by special function calls that delegate the admin tasks to OS
//   heap objects remain valid across several function calls
//   the administration is expensive

struct MyStruct {
	n int
}

struct RefStruct {
	r &MyStruct
}

fn main() {
	q, w := f()
	println(typeof(q).name)
	println('q: $q.r.n, w: $w.n')
}

fn f() (RefStruct, &MyStruct) {
	// stored on the stack since it's address never leaves the function f()
	a := MyStruct{  
		n: 1
	}
	
	// referenced by `e` which is returned, so `b` is heap allocated
	b := MyStruct{
		n: 2
	}
	
	// a reference of `c` is returned, so `c` is heap allocated
	c := MyStruct{
		n: 3
	}
	
	e := RefStruct{
		r: &b
	}
	
	x := a.n + c.n
	println('x: $x')
	
	return e, &c
}
